import json
import cbor
import time

from pydantic import BaseModel

# 递归对dict的key按字母顺序排序


def sorted_dict_full(adict: dict):
    items = sorted(adict.items())
    newdict = dict()
    for item in items:
        # print("item:",item)
        sortitem = item[1]
        if isinstance(sortitem, dict):
            sortitem = sorted_dict_full(sortitem)
        newdict[item[0]] = sortitem
    return newdict


class DIDCodec:
    # 默认的时间格式，"2020-04-22T10:37:22Z"
    timefmt = "%Y-%m-%dT%H:%M:%SZ"

    def from_timestr(timestr):
        return time.strptime(timestr, DIDCodec.timefmt)

    def to_timestr(tmobject):
        return time.strftime(DIDCodec.timefmt, tmobject)

    def to_json(doc: BaseModel, indent=None, do_sort=False):
        datadict = doc.dict(exclude_none=True)  # exclude_none 忽略None字段
        if do_sort is True:
            datadict = sorted_dict_full(datadict)
        docstr = json.dumps(datadict, indent=indent)
        return docstr

    def to_jsonld(doc: BaseModel, indent=None, context=None, do_sort=False):
        newdoc = dict()
        if context is not None:
            newdoc["@Context"] = context
        datadict = doc.dict(exclude_none=True)  # exclude_none 忽略None字段
        if do_sort is True:
            datadict = sorted_dict_full(datadict)
        newdoc.update(datadict)
        return json.dumps(newdoc, indent=indent)

    def to_cbor(doc: BaseModel, do_sort=False):
        datadict = doc.dict(exclude_none=True)  # exclude_none 忽略None字段
        if do_sort is True:
            datadict = sorted_dict_full(datadict)
        cbordata = cbor.dumps(datadict)
        return cbordata

    def from_dict(dictdata, modelname):
        # 作为pydantic的BaseModel，这样的实例化会检查变量名字和类型，比较严谨
        doc = modelname(**dictdata)
        return doc

    def from_json(jsonstr: str, modelname):
        jsonobj = json.loads(jsonstr)
        return DIDCodec.from_dict(jsonobj, modelname)

    def from_cbor(cbordata, modelname):
        cborobj = cbor.loads(cbordata)
        doc = DIDCodec.from_dict(cborobj, modelname)
        return doc
